繼昨天完成的Quiz App(原生JS版)後,這次要來看的是Vue版:Demo Link | CodePen(原生JS版)
Vue版和原生JS版本不同的地方
Vue檔案部分有三個
src/views/QuizApp.vue
<template>
<home-btn />
<main
class="
bg-gradient-to-tl
from-blue-200
via-yellow-100
to-gray-300
w-[100vw]
flex
justify-center
items-center
overflow-hidden
mr-0
"
>
<div
class="bg-white rounded-lg w-[600px] drop-shadow-lg"
id="quiz-container"
>
<div id="quiz-header" class="p-12 flex flex-col justify-around">
<questions
// 答題數小於題庫長度時才顯示子元件
v-if="quizAnswered < quizData.length"
// 將題庫quizData送進子元件
:quizData="quizData"
// 將答題數送進子元件
:quizAnswered="quizAnswered"
// 將屬性check-answer送進子元件,當子元件emit上來時,便會觸發函式checkAnswer
@check-answer="checkAnswer"
/>
// 子元件questions不顯示時便顯示result
<result v-else :score="score" />
<button
id="Reset"
class="
bg-purple-600
hover:bg-purple-800
text-white
tracking-wide
font-normal
w-[15%]
cursor-pointer
mt-3
rounded-3xl
"
// 當答題數等於題庫陣列長度時,才顯示reset按鈕
v-if="quizAnswered === quizData.length"
// 點擊按鈕,觸發reset,回到第一題。
@click.prevent="reset"
>
Reset
</button>
</div>
</div>
</main>
</template>
<script>
import homeBtn from '../components/HomeBtn.vue'
import Questions from '../components/quiz-app/Questions.vue'
import Result from '../components/quiz-app/Result.vue'
import { ref } from 'vue'
export default {
name: '#15. Quiz App',
components: {
homeBtn,
Questions,
Result,
},
setup() {
// 建立題庫
const quizData = ref([
{
q: 'Which language runs in a web browser?',
answers: [
{
text: 'Java',
is_correct: false,
},
{
text: 'C',
is_correct: false,
},
{
text: 'Python',
is_correct: false,
},
{
text: 'Javascript',
is_correct: true,
},
],
},
// ...略(共四題)
])
const quizAnswered = ref(0)
const score = ref(0)
// 若選中的answer.is_correct為true,則score + 1
const checkAnswer = (is_correct) => {
if (is_correct) {
score.value++
}
// 判斷是否要給分後,進到下一題
quizAnswered.value++
}
// 重設分數與答題數
const reset = () => {
score.value = 0
quizAnswered.value = 0
}
return {
quizData,
quizAnswered,
score,
checkAnswer,
reset,
}
},
}
</script>
src/components/quiz-app/Questions.vue
<template>
<div
class="p-0 flex flex-col"
id="single-question"
// 這裡使用v-for遍歷題庫,將資料渲染進模板中
v-for="(question, index) in quizData"
// 只有目前要作答的題目才會顯示
v-show="quizAnswered === index"
:key="question.q"
>
<h2 class="text-center mb-3 text-3xl font-semibold">
// 對照題庫資料結構,顯示題目
{{ question.q }}
</h2>
<div
class="
text-xl
font-thin
text-center
my-2
hover:bg-gray-100 hover:font-normal
rounded-sm
"
// 按照資料結構,每個題目中還有一個陣列answers,在此也用v-for渲染出來
v-for="answer in question.answers"
:key="answer.text"
// 被選中的答案,會把answer裡的is_correct帶進函式selectAnswer
@click.prevent="selectAnswer(answer.is_correct)"
>
<div class="cursor-pointer py-3 border-2 rounded-lg">
// 對照題庫資料結構,顯示答案選項
{{ answer.text }}
</div>
</div>
</div>
<div class="progress mt-2 h-[40px] relative bg-pink-50">
<div
class="bar bg-pink-300 h-[40px] transition-all duration-300"
// 這裡運用答題數與題庫陣列長度的運算,即時改變style所綁定的width
// 達成進度條顯示效果
:style="{ width: `${(quizAnswered / quizData.length) * 100}%` }"
></div>
<div class="status absolute top-[10px] w-full left-0 text-center">
{{ quizAnswered }} out of {{ quizData.length }} quiz answered
</div>
</div>
</template>
<script>
export default {
name: 'Questions',
// 用emits引入父元件的check-answer
emits: ['check-answer'],
// 從父元件引入props資料quizData和quizAnswered
props: {
quizData: {
type: Object,
required: true,
},
quizAnswered: {
type: Number,
required: true,
},
},
setup(props, { emit }) {
// 建立selectAnswer函式,並引入參數is_correct,然後透過emit中的check-answer,將is_correct送到父元件,來執行父元件的checkAnswer函式
const selectAnswer = (is_correct) => {
emit('check-answer', is_correct)
}
return {
selectAnswer
}
},
}
</script>
src/components/quiz-app/Result.vue
Result.vue部分還有一些地方可以優化,之後會再進一步調整。
<template>
<div class="result">
<div class="title">Glad you finish this quiz! </div>
// 這裡單純從父元件取得score,然後渲染進模板中
<div class="desc">Your score is: {{ score }} / 4</div>
</div>
</template>
<script>
export default {
name: 'results',
props: {
score: {
type: Number,
required: true,
}
},
setup() {
},
}
</script>
寫文章寫一半發現忘了時間,PO完這篇已經過了6秒,今年鐵人賽沒挑戰成功...Orz
沒關係繼續努力!